/*
 * Copyright (c) 2011, Yawning <yawninglol at gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   * Neither the name of the Mew Developers nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

package yawning.mew.sim;

import yawning.mew.character.CharacterModel;
import yawning.mew.target.Target;

public class SimulationTarget {
	public class TargetData {
		public class DeathEvent extends SimulationEvent {
			private TargetData mTargetData;

			DeathEvent(SimulationEngine simEngine, TargetData targetData) {
				super(simEngine);
				mTargetData = targetData;

				double timeToDie = mTargetData.getSimulationTarget().getTarget().getTimeToDie();
				if (timeToDie > 0.0)
					schedule(timeToDie);
				else if (timeToDie == 0.0)
					schedule(mSimEngine.getEndTime() - mSimEngine.getSimTime());
				else
					throw new RuntimeException("Attempted to spawn a target with negative lifespan.");
			}

			@Override
			public void onExpire() {
				TargetData.this.getSimulationTarget().getTargetCastEv().cancel();
				TargetData.this.getSimulationTarget().getTargetSwingEv().cancel();
				TargetData.this.getSimulationTarget().getTargetYellowEv().cancel();
				TargetData.this.getSimulationTarget().getTargetMagicEv().cancel();
				TargetData.this.getSimulationTarget().getTargetStunEv().cancel();
				TargetData.this.onTargetDeath();
			}
		}

		public DeathEvent mDeathEv;
		double mArmorMajorMult = 1.0;	// Feral FF, Sunder Armor, etc
		double mArmorMinorMult = 1.0;	// Shattering Throw
		private double mArmorDR;

		private boolean mArmorCacheValid = false;

		public TargetData(SimulationEngine simEngine) {
			mDeathEv = new DeathEvent(simEngine, this);
		}

		public SimulationTarget getSimulationTarget() {
			return SimulationTarget.this;
		}

		public void setArmorMajorMult(double mult) {
			mArmorCacheValid = false;
			mArmorMajorMult = mult;
		}

		public void setArmorMinorMult(double mult) {
			mArmorCacheValid = false;
			mArmorMinorMult = mult;
		}

		public double getArmor() {
			return mTarget.getBaseArmor() * mArmorMajorMult * mArmorMinorMult;
		}

		public double getArmorDR(int playerLevel) {
			if (mArmorCacheValid) {
				return mArmorDR;
			}

			mArmorDR = CharacterModel.calculateArmorDR(getArmor(), playerLevel);
			mArmorCacheValid = true;

			return mArmorDR;
		}

		public void onTargetDeath() {
			// Override this.
		}

		// Implementations should extend this to add more things to track.
	}

	private Target mTarget;
	private TargetData mTargetData;
	private SimulationEngine mSimEngine;

	// Target specific events
	private SimulationEventTargetCast mTargetCastEv;
	private SimulationEventTargetSwing mTargetSwingEv;
	private SimulationEventTargetYellow mTargetYellowEv;
	private SimulationEventTargetMagic mTargetMagicEv;
	private SimulationEventTargetStun mTargetStunEv;

	public SimulationTarget(Target target, SimulationEngine simEngine) {
		mTarget = target;
		mSimEngine = simEngine;

		// Schedule hostile events.
		mTargetCastEv = new SimulationEventTargetCast(mSimEngine, this);
		mTargetSwingEv = new SimulationEventTargetSwing(mSimEngine, this);
		mTargetYellowEv = new SimulationEventTargetYellow(mSimEngine, this);
		mTargetMagicEv = new SimulationEventTargetMagic(mSimEngine, this);
		mTargetStunEv = new SimulationEventTargetStun(mSimEngine, this);
	}

	public Target getTarget() {
		return mTarget;
	}

	public TargetData getTargetData() {
		return mTargetData;
	}

	public void setTargetData(TargetData targetData) {
		mTargetData = targetData;
	}

	public SimulationEventTargetCast getTargetCastEv() {
		return mTargetCastEv;
	}

	public SimulationEventTargetSwing getTargetSwingEv() {
		return mTargetSwingEv;
	}

	public SimulationEventTargetYellow getTargetYellowEv() {
		return mTargetYellowEv;
	}

	public SimulationEventTargetMagic getTargetMagicEv() {
		return mTargetMagicEv;
	}

	public SimulationEventTargetStun getTargetStunEv() {
		return mTargetStunEv;
	}
}
